package service

import (
	"context"
	"errors"
	"github.com/gogf/gf/v2/crypto/gsha1"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/text/gregex"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/gogf/gf/v2/util/grand"
	"github.com/tiger1103/gfast/v3/internal/app/plugins/sms/dao"
	"github.com/tiger1103/gfast/v3/internal/app/plugins/sms/driver"
	"github.com/tiger1103/gfast/v3/internal/app/plugins/sms/model"
	"time"
)

type yunxinconfig struct {
	NoticeUrl     string // 通知类短信的发送地址
	SendCodeUrl   string // 验证码短信的发送地址
	VerifyCodeUrl string // 验证码短信的验证地址
	AppKey        string
	AppSecret     string
	Templates     g.Map
}

type yunxinService struct {
	YunxinConfig *yunxinconfig
}

var Yunxin = new(yunxinService)

// SendTemplateSms 通知类模板短信
func (s *yunxinService) SendTemplateSms(args *driver.SendSmsArgs) (err error) {
	err = s.setConfig(args.Ctx)
	if err != nil {
		return
	}
	if s.YunxinConfig.NoticeUrl == "" {
		err = errors.New("请配置网易云模板短信发送地址")
		return
	}
	if len(args.Mobiles) < 1 {
		err = errors.New("手机号不能为空")
		return
	}
	var templateId string
	if v, ok := s.YunxinConfig.Templates["notice"]; ok {
		templateId = gconv.String(v)
	}
	if args.TemplateId != "" {
		templateId = args.TemplateId
	}
	// 清除手机号中的空格和横杆
	for i, mobile := range args.Mobiles {
		args.Mobiles[i], err = gregex.ReplaceString("\\s|-", "", mobile)
		if err != nil {
			return
		}
	}
	return s.sendSms(args.Ctx,s.YunxinConfig.NoticeUrl, g.Map{
		"templateid": templateId,
		"mobiles":    args.Mobiles,
		"params":     args.Params,
	}, "SendTemplateSms")

}

// SendVerifyCode 发送验证码短信
func (s *yunxinService) SendVerifyCode(args *driver.SendSmsArgs) (err error) {
	err = s.setConfig(args.Ctx)
	if err != nil {
		return
	}
	template := ""
	if v, ok := s.YunxinConfig.Templates["verifyCode"]; ok {
		template = gconv.String(v)
	}
	if len(args.Mobiles) < 1 {
		err = errors.New("手机号码不能为空")
		return
	}
	mobile, _ := gregex.ReplaceString("\\s|-", "", args.Mobiles[0])
	authCode, err := Sms.CreateVerifyCode(args.Ctx,mobile)
	if err != nil {
		return
	}
	if s.YunxinConfig.SendCodeUrl == "" {
		err = errors.New("请配置网易云验证码短信发送地址")
		return
	}
	return s.sendSms(args.Ctx,s.YunxinConfig.SendCodeUrl, g.Map{
		"templateid": template,
		"mobile":     mobile,
		"authCode":   authCode,
	}, "SendVerifyCode")
}

func (s *yunxinService) CheckVerifyCode(ctx context.Context,mobile string, code string) (err error) {
	// 不到网易服务器确认，只在本地认证
	return Sms.CheckVerifyCode(ctx,mobile, code)
}

// 设置配置
func (s *yunxinService) setConfig(ctx context.Context) (err error) {
	if s.YunxinConfig == nil {
		s.YunxinConfig, err = s.GetConfig(ctx)
	}
	return
}

// GetConfig 获取网易云信的配置
func (s *yunxinService) GetConfig(ctx context.Context) (yunxinConfig *yunxinconfig, err error) {
	smsConfig, err := dao.PluginSmsConfig.Ctx(nil).Where("sms_type", "yunxin").One()
	if err != nil {
		g.Log().Error(ctx,err)
		return
	}
	if smsConfig.IsEmpty() {
		err = errors.New("短信配置信息不存在")
		g.Log().Error(ctx,err)
		return
	}

	if v, ok := smsConfig["config"]; ok {
		err = gconv.Struct(v, &yunxinConfig)
		if err != nil {
			g.Log().Error(ctx,err)
			return
		}
	}
	if yunxinConfig.AppKey == "" || yunxinConfig.AppSecret == "" {
		err = errors.New("网易云信配置有错误")
		g.Log().Error(ctx,err)
		return
	}
	return
}

// 发送短信
func (s *yunxinService) sendSms(ctx context.Context,url string, data g.Map, smsType string) (err error) {
	var (
		nonce   = grand.S(16)
		curTime = gconv.String(gtime.Now().UTC().Timestamp())
	)
	result := g.Client().Timeout(5*time.Second).Header(map[string]string{
		"AppKey":       s.YunxinConfig.AppKey,
		"Nonce":        nonce,
		"CurTime":      curTime,
		"CheckSum":     gsha1.Encrypt(s.YunxinConfig.AppSecret + nonce + curTime),
		"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
	}).PostContent(ctx,url, data)
	res := gconv.Map(result)
	if statusCode, ok := res["code"]; ok {
		//写入发送日志
		mobile := make([]string, 0)
		params := make([]string, 0)
		if m, ok := data["mobile"]; ok {
			mobile = append(mobile, gconv.String(m))
		} else if m, ok := data["mobiles"]; ok {
			mobile = gconv.SliceStr(m)
		}
		if p, ok := data["params"]; ok {
			params = gconv.SliceStr(p)
		} else if p, ok := data["authCode"]; ok {
			params = append(params, gconv.String(p))
		}
		err = Sms.WriteLog(ctx,&model.ClientArg{
			SmsType:    "yunxin",
			MsgType:    smsType,
			Result:     gconv.String(res),
			TemplateId: gconv.String(data["templateid"]),
			Mobiles:    mobile,
			Params:     params,
		})
		if err!=nil{
			return
		}
		if gconv.Int(statusCode) == 200 {
			return
		} else {
			if msg, ok2 := res["msg"]; ok2 {
				return errors.New("发送失败：" + gconv.String(msg))
			}
		}
	}
	return errors.New("未知错误,发送短信失败")
}
